package com.hongji.lms5xxcommunicator.grid.update;

import com.hongji.lms5xxcommunicator.grid.core.RasterState;

/**
 * <p>
 * “单点替换，否则按均值合并”的更新策略实现。<br/>
 * 对同一帧内落入同一格的点先做批内聚合（sum / count），随后与已有栅格状态合并：
 * </p>
 *
 * <ul>
 *   <li><b>单点命中：</b>若某格本帧仅有 1 个点，则直接用该点值替换该格（更“锐利”）。</li>
 *   <li><b>多点命中：</b>若某格本帧有多点，则使用本帧的格内均值与历史做“加权均值”合并。</li>
 * </ul>
 *
 * <p><b>用法</b></p>
 * <ol>
 *   <li>创建策略实例并在栅格写入阶段调用 {@link #apply(RasterState, double[], double[], double[])}。</li>
 *   <li>本策略会就地更新 {@link RasterState#sumZ}、{@link RasterState#count}、{@link RasterState#meanZ} 与 {@link RasterState#hasData}。</li>
 *   <li>调用方应保证 {@code xs}/{@code ys}/{@code zs} 三者长度一致。</li>
 * </ol>
 *
 * @see UpdateStrategy
 * @see com.hongji.lms5xxcommunicator.grid.update.MeanUpdateStrategy
 * @see RasterState
 */
public class ReplaceSingleElseMeanStrategy implements UpdateStrategy {

    /**
     * <p>
     * 将一批点应用到 {@link RasterState}。该方法分两步：
     * </p>
     * <ol>
     *   <li><b>帧内聚合：</b>将当前帧点云按 (r,c) 累加到临时缓冲 {@code bSum}/{@code bCnt}。</li>
     *   <li><b>写回合并：</b>按“单点替换 / 多点均值合并”的规则写回到 {@link RasterState}。</li>
     * </ol>
     *
     * <p><b>边界与健壮性</b></p>
     * <ul>
     *   <li>越界点通过 {@link RasterState#inBounds(int, int)} 过滤。</li>
     *   <li>跳过 {@code NaN}/{@code Infinity} 的输入值。</li>
     *   <li>当历史该格无数据时，直接按本帧统计写入。</li>
     * </ul>
     *
     * @param s   目标栅格状态（不可为 {@code null}）
     * @param xs  点集 X 数组
     * @param ys  点集 Y 数组
     * @param zs  点集 Z 数组
     */
    @Override
    public void apply(RasterState s, double[] xs, double[] ys, double[] zs) {
        if (s == null || xs == null || ys == null || zs == null) return;
        if (xs.length != ys.length || xs.length != zs.length) return;

        final int rows = s.spec.rows;
        final int cols = s.spec.cols;

        // 帧内缓冲：同一帧内先做 (r,c) 维度聚合
        final double[][] bSum = new double[rows][cols];
        final int[][]    bCnt = new int[rows][cols];

        // 1) 帧内聚合
        for (int i = 0; i < xs.length; i++) {
            final double x = xs[i];
            final double y = ys[i];
            final double z = zs[i];

            // 跳过无效值，避免污染
            if (Double.isNaN(x) || Double.isNaN(y) || Double.isNaN(z)) continue;
            if (Double.isInfinite(x) || Double.isInfinite(y) || Double.isInfinite(z)) continue;

            final int r = s.spec.toRow(y);
            final int c = s.spec.toCol(x);
            if (!s.inBounds(r, c)) continue;

            bSum[r][c] += z;
            bCnt[r][c] += 1;
        }

        // 2) 写回合并
        for (int r = 0; r < rows; r++) {
            final double[] sumRow = s.sumZ[r];
            final int[]    cntRow = s.count[r];
            final double[] meanRow = s.meanZ[r];
            final boolean[] hasRow = s.hasData[r];

            for (int c = 0; c < cols; c++) {
                final int cntAdd = bCnt[r][c];
                if (cntAdd == 0) continue;

                if (cntAdd == 1) {
                    // 单点：直接替换
                    final double v = bSum[r][c];
                    sumRow[c] = v;
                    cntRow[c] = 1;
                    meanRow[c] = v;
                    hasRow[c] = true;
                } else {
                    // 多点：与历史做“加权均值”合并
                    final double frameMean = bSum[r][c] / cntAdd;

                    if (!hasRow[c] || cntRow[c] <= 0) {
                        // 历史无数据：直接写入“本帧统计”
                        sumRow[c]  = frameMean * cntAdd;
                        cntRow[c]  = cntAdd;
                        meanRow[c] = frameMean;
                        hasRow[c]  = true;
                    } else {
                        // 历史有数据：新旧合并
                        final double newSum = sumRow[c] + frameMean * cntAdd;
                        final int    newCnt = cntRow[c] + cntAdd;

                        sumRow[c]  = newSum;
                        cntRow[c]  = newCnt;
                        meanRow[c] = newSum / newCnt; // newCnt > 0 恒成立
                        hasRow[c]  = true;
                    }
                }
            }
        }
    }
}
